/' Copyright (c) 2007 Scott Lembcke
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 '/

extern "C"

'' Forward declarations required for defining other structs.
type cpShapeClass_ as cpShapeClass

type cpSegmentQueryInfo
	as cpShape_ ptr	shape	'' shape that was hit, NULL if no collision
	as cpFloat		t		'' Distance along query segment, will always be in the range [0, 1].
	as cpVect		n		'' normal of hit surface
end type

enum cpShapeType
	CP_CIRCLE_SHAPE
	CP_SEGMENT_SHAPE
	CP_POLY_SHAPE
	CP_NUM_SHAPES
end enum

type cpShapeCacheDataImpl as function cdecl( byval as cpShape_ ptr, byval as cpVect, byval as cpVect ) as cpBB
type cpShapeDestroyImpl as sub cdecl( byval as cpShape_ ptr )
type cpShapePointQueryImpl as function cdecl( byval as cpShape_ ptr, byval as cpVect ) as cpBool
type cpShapeSegmentQueryImpl as sub cdecl( byval as cpShape_ ptr, byval as cpVect, byval as cpVect, byval as cpSegmentQueryInfo ptr )

'' Shape class. Holds function pointers and type data.
type cpShapeClass
	as cpShapeType type
	
	as cpShapeCacheDataImpl cacheData
	as cpShapeDestroyImpl destroy
	as cpShapePointQueryImpl pointQuery
	as cpShapeSegmentQueryImpl segmentQuery
end type


' Basic shape struct that the others inherit from.
type cpShape
	'' * FreeBASIC Note: The klass is private - DO NOT DIRECTLY MODIFY UNDER PENALTY OF DEATH
	''					(or something just really really bad, like segfaults!)
	as const cpShapeClass ptr	klass
	
	'' The rigid body this collision shape is attached to.
	as cpBody ptr				body

	'' The current bounding box of the shape.
	as cpBB						bb
	
	'' Sensor flag.
	'' Sensor shapes call collision callbacks but don't produce collisions.
	as cpBool					sensor
	
	'' Coefficient of restitution. (elasticity)
	as cpFloat					e
	'' Coefficient of friction.
	as cpFloat					u
	'' Surface velocity used when solving for friction.
	as cpVect					surface_v

	'' User definable data pointer.
	'' Generally this points to your the game object class so you can access it
	'' when given a cpShape reference in a callback.
	as cpDataPointer			data
	
	'' Collision type of this shape used when picking collision handlers.
	as cpCollisionType			collision_type
	'' Group of this shape. Shapes in the same group don't collide.
	as cpGroup					group
	'' Layer bitmask for this shape. Shapes only collide if the bitwise and of their layers is non-zero.
	as cpLayers					layers
	
	'' * FreeBASIC Note: These are private fields - DO NOT DIRECTLY MODIFY!!!!!!
	as cpSpace_ ptr				space
	
	as cpShape_ ptr				next
	as cpShape_ ptr				prev
	
	as cpHashValue				hashid

end type

'' Destroy a shape.
declare sub cpShapeDestroy cdecl alias "cpShapeDestroy" ( byval shape as cpShape ptr )
'' Destroy and free a shape.
declare sub cpShapeFree cdecl alias "cpShapeFree" ( byval shape as cpShape ptr )

'' Update, cache and return the bounding box of a shape based on the body it's attached to.
declare function cpShapeCacheBB cdecl alias "cpShapeCacheBB" ( byval shape as cpShape ptr ) as cpBB
'' Update, cache and return the bounding box of a shape with an explicit transformation.
declare function cpShapeUpdate cdecl alias "cpShapeUpdate" ( byval as cpShape ptr, byval as cpVect, byval as cpVect ) as cpBB

'' Test if a point lies within a shape.
declare function cpShapePointQuery cdecl alias "cpShapePointQuery" ( byval as cpShape ptr, byval as cpVect ) as cpBool

#define CP_DefineShapeStructGetter(t, member, n) function cpShapeGet##n( byval shape as const cpShape ptr) as t : return shape->member : end function

#macro CP_DefineShapeStructSetter(t, member, n, activates)
sub cpShapeSet##n cdecl( byval shape as cpShape ptr, byval value as t )
	if activates then cpBodyActivate( shape->body)
	shape->member = value
end sub
#endmacro

#macro CP_DefineShapeStructProperty(t, member, n, activates)
CP_DefineShapeStructGetter(t, member, n)
CP_DefineShapeStructSetter(t, member, n, activates)
#endmacro

CP_DefineShapeStructGetter(cpBody_ ptr, body, Body)
declare sub cpShapeSetBody cdecl alias "cpShapeSetBody" ( byval as cpShape ptr, byval as cpBody_ ptr )

CP_DefineShapeStructGetter(cpBB, bb, BB)
CP_DefineShapeStructProperty(cpBool, sensor, Sensor, cpTrue)
CP_DefineShapeStructProperty(cpFloat, e, Elasticity, cpFalse)
CP_DefineShapeStructProperty(cpFloat, u, Friction, cpTrue)
CP_DefineShapeStructProperty(cpVect, surface_v, SurfaceVelocity, cpTrue)
CP_DefineShapeStructProperty(cpDataPointer, data, UserData, cpFalse)
CP_DefineShapeStructProperty(cpCollisionType, collision_type, CollisionType, cpTrue)
CP_DefineShapeStructProperty(cpGroup, group, Group, cpTrue)
CP_DefineShapeStructProperty(cpLayers, layers, Layers, cpTrue)

'' When initializing a shape, it's hash value comes from a counter.
'' Because the hash value may affect iteration order, you can reset the shape ID counter
'' when recreating a space. This will make the simulation be deterministic.
declare sub cpResetShapeIdCounter ()

'' Perform a segment query against a shape. @c info must be a pointer to a valid cpSegmentQueryInfo structure.
declare function cpShapeSegmentQuery( byval as cpShape ptr, byval as cpVect, byval as cpVect, byval as cpSegmentQueryInfo ptr ) as cpBool

'' Get the hit point for a segment query.
function cpSegmentQueryHitPoint cdecl( byval start as const cpVect, byval e as const cpVect, byval info as const cpSegmentQueryInfo ) as cpVect
	return cpvlerp( start, e, info.t )
end function

'' Get the hit distance for a segment query.
function cpSegmentQueryHitDist cdecl( byval start as const cpVect, byval e as const cpVect, byval info as const cpSegmentQueryInfo ) as cpFloat
	return cpvdist( start, e ) * info.t
end function

#define CP_DeclareShapeGetter(struct, t, n) declare function struct##Get##n( byval shape as const cpShape ptr ) as t

' Circle shape structure.
type cpCircleShape
	as cpShape	shape
		
	' Center. (body space coordinates)
	as cpVect	c
	' Radius.
	as cpFloat	r
	
	' Transformed center. (world space coordinates)
	as cpVect	tc
end type

'' Allocate a circle shape.
declare function cpCircleShapeAlloc ( ) as cpCircleShape ptr
'' Initialize a circle shape.
declare function cpCircleShapeInit ( byval circle as cpCircleShape ptr, byval body as cpBody ptr, byval radius as cpFloat, byval offset as cpVect ) as cpCircleShape ptr
'' Allocate and initialize a circle shape.
declare function cpCircleShapeNew ( byval body as cpBody ptr, byval radius as cpFloat, byval offset as cpVect ) as cpShape ptr

CP_DeclareShapeGetter(cpCircleShape, cpVect, Offset)
CP_DeclareShapeGetter(cpCircleShape, cpFloat, Radius)

'' Segment shape structure.
type cpSegmentShape
	as cpShape	shape
	
	'' Endpoints and normal of the segment. (body space coordinates)
	as cpVect	a, b, n
	'' Radius of the segment. (Thickness)
	as cpFloat	r

	'' Transformed endpoints and normal. (world space coordinates)
	as cpVect	ta, tb, tn
end type

'' Allocate a segment shape.
declare function cpSegmentShapeAlloc ( ) as cpSegmentShape ptr
'' Initialize a segment shape.
declare function cpSegmentShapeInit ( byval as cpSegmentShape ptr, byval as cpBody ptr, byval as cpVect, byval as cpVect, byval as cpFloat ) as cpSegmentShape ptr
'' Allocate and initialize a segment shape.
declare function cpSegmentShapeNew ( byval as cpBody ptr, byval as cpVect, byval as cpVect, byval as cpFloat ) as cpShape ptr

CP_DeclareShapeGetter(cpSegmentShape, cpVect, A)
CP_DeclareShapeGetter(cpSegmentShape, cpVect, B)
CP_DeclareShapeGetter(cpSegmentShape, cpVect, Normal)
CP_DeclareShapeGetter(cpSegmentShape, cpFloat, Radius)

end extern
